package com.partner.boot.common;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 过滤器：通过时间窗口函数限制接口访问，防止接口恶意访问
 */

@Component
@Slf4j
public class MyFilter implements Filter {

    // 1秒 之内只允许通过 100个 请求

    private static volatile long startTime = System.currentTimeMillis(); // 初始化时间

    private static final long windowTime = 1000L; // 时间窗口，也就是允许多长时间内可访问接口的限制时间

    private static final int door = 100; // 限制访问的接口阈值

    private static final AtomicInteger bear = new AtomicInteger(0); // 桶，每次访问接口，就往桶内 + 1

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        int count = bear.incrementAndGet(); // 只要来一个人，就+1
        if (count == 1) { // 并发安全的 第一个请求发生的时间
            startTime = System.currentTimeMillis();
        }
        // 发生了请求
        long now = System.currentTimeMillis(); // 每个发生请求的当前时间
        log.info("拦截了请求，count：{}", count);
        log.info("时间窗口，{}ms，count：{}", (now - startTime), count);
        if (now - startTime <= windowTime) { // 如果每次发生请求的当前时间 - 第一次请求的时间 大于窗口时间，也就是在1秒内，可以访问的接口
            if (count > door) { // 如果超过访问阈值
                // 就要进行限制  限流操作
                log.info("关门放狗成功，拦截了请求，count：{}",count);
                // 接口访问太过频繁友好提示
                HttpServletResponse response = (HttpServletResponse) servletResponse;
                response.setStatus(HttpStatus.OK.value());
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
                response.getWriter().print(JSONUtil.toJsonStr(Result.error("402", "接口访问太频繁")));
                return;
            }
        } else {
            // 重新进入下一个窗口
            startTime = System.currentTimeMillis();
            bear.set(1);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
